<?php
namespace Core\Datastorage\Tools;

require_once(DATASTORAGE_DIR . 'dbtypes.enum.php');

use Core\CoreCommons\Logger as Logger;
use Core;

/**
* Generate the database from mapping files
*
* @method void Reset($databaseName)
* @method string BuildTable($mappingFilePath)
* @method void SetTableOnDataBase($databaseName, $sql)
* @method array GetMappingFiles($mappingRootPath)
*/
class ResetMappings
{
	/*
	* @property string $defaultValuesScript The default values script
	*/
	private $defaultValuesScript;
	
	/*
	* The default constructor
	*/
	public function __construct() 
	{
		$this->defaultValuesScript = DATASTORAGE_DIR . "/Scripts/default_values.sql";
	}
	
	/* 
	* Reset the database with current mapping
	*
	* @param string $databaseName The database name
	*/
	public function Reset($databaseName)
	{
		try
		{
			Logger::Debug("Reset all mappings on $databaseName");
			
			$mappingRootPath = DATASTORAGE_DIR . '/Mappings/';
			$files = $this->GetMappingFiles($mappingRootPath);
			
			foreach ($files as $file)
			{
				$sql = $this->BuildTable($mappingRootPath . $file);
				$this->SetTableOnDataBase($databaseName, $sql);
			}
			
			if(file_exists($this->defaultValuesScript))
			{
				$script = \file_get_contents($this->defaultValuesScript);
			
				$this->executeScript($script, $databaseName);
			}
			else
			{
				Logger::Warning("Script $this->defaultValuesScript doen't exist");
			}
		}
		catch (Exception $ex) 
		{
			Logger::Error($ex->getMessage());
		}
	}

	/*
	* Build a SQL table from a xml mapping file
	* 
	* @param string $mappingFilePath The mapping file path
	*
	* @todo Manage the foreign key
	*
	* @result string The SQL to build the table
	*/
	private function BuildTable($mappingFilePath)
	{
		Logger::Debug("CreateTable from $mappingFilePath");
		$xml = new \DomDocument();
		$xml->load($mappingFilePath);
		$mapping = $xml->getElementsByTagName('mapping');
		
		$properties = $xml->getElementsByTagName('property');

		$element = $mapping->item(0);
		$tablename = $element->attributes->getNamedItem('table')->nodeValue;
		$primary = $element->attributes->getNamedItem('primarycolumn')->nodeValue;

		Logger::Debug("The table name is  : " . $tablename);
		$sql = 'CREATE TABLE `'. $tablename .'` (';
		foreach ($properties as $property)
		{
			$columnName = $property->attributes->getNamedItem('Column')->nodeValue;
			
			$type = $this->GetDBType($property->attributes->getNamedItem('Type')->nodeValue, false);
			
			$isNullable = $property->attributes->getNamedItem('IsNullable')->nodeValue;
			
			$nullProperty = "NOT NULL";
			if($isNullable == "1")
			{
				$nullProperty = "NULL";
			}
			
			if($columnName == $primary)
			{
				$sql .= sprintf('`%s` %s NOT NULL AUTO_INCREMENT,', $columnName, $type);
			}
			else
			{
				$sql .= sprintf('`%s` %s %s,', $columnName, $type, $nullProperty);
			}
		}
		$properties = $xml->getElementsByTagName('many-to-one');
		foreach ($properties as $property)
		{
			$columnName = $property->attributes->getNamedItem('Column')->nodeValue;
			
			$type = $this->GetDBType($property->attributes->getNamedItem('Type')->nodeValue, true);
			
			$isNullable = $property->attributes->getNamedItem('IsNullable')->nodeValue;
			
			$nullProperty = "NOT NULL";
			if($isNullable == "1")
			{
				$nullProperty = "NULL";
			}
			
			if($columnName == $primary)
			{
				$sql .= sprintf('`%s` %s NOT NULL AUTO_INCREMENT,', $columnName, $type);
			}
			else
			{
				$sql .= sprintf('`%s` %s %s,', $columnName, $type, $nullProperty);
			}
		}
		
		$sql .= 'PRIMARY KEY (`'. $primary .'`)';
		$sql .= ') ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;';
		
		Logger::Info("The table " . $tablename . " was successfully built");
		
		return $sql;
	}
	
	/*
	* Insert the table on database
	*
	* @param string $databaseName The database name
	* @param string $sql The sql to execute
	*
	*/
	private function SetTableOnDataBase($databaseName, $sql)
	{
		$session = Core\Datastorage\Session::Singleton();
		$session->Open();

		// Select the current database
		$db_selected = mysql_select_db($databaseName);
		if (!$db_selected) 
		{
			throw new Exception('Cannot select database $databaseName : ' . mysql_error());
		}
		
		Logger::Debug("SQL creation table  : " . $sql);
		if (mysql_query($sql)) 
		{
			Logger::Info("Table was successfully inserted");
		} 
		else 
		{
			Logger::Error("Insert table failed : " . mysql_error());
		}
		
		$session->Close();
	}
	
	/*
	* Return mapping file list
	*
	* @param string $mappingRootPath The mapping root path
	*
	* @result array The mapping files
	*/
	private function GetMappingFiles($mappingRootPath)
	{
		Logger::Debug("GetMappingFiles from : " . $mappingRootPath);
		return array_diff(scandir($mappingRootPath), array('..', '.'));
	}
	
	/*
	* Get the DB type from the PHP type
	*
	* @param string $type  The type name
	* @param boolean $isComplex Is a complex type 
	* 
	* @return string The database type translation form PHP type
	*/
	private function GetDBType($type, $isComplex = false)
	{
		$result = null;
		$type =  strtolower($type);
	
		if($isComplex == true)
		{
			$result = Core\Datastorage\DBTypes::DBInteger;
		}
		else
		{
			if($type == "int" || $type == "int32")
			{
				$result = Core\Datastorage\DBTypes::DBInteger;
			}
			else if($type == "string")
			{
				$result = Core\Datastorage\DBTypes::DBString;
			}
			else if($type == "datetime")
			{
				$result = Core\Datastorage\DBTypes::DBDatetime;
			}
			else if($type == "float" || $type == "single")
			{
				$result = Core\Datastorage\DBTypes::DBFloat;
			}
			else if($type == "bool" || $type == "boolean")
			{
				$result = Core\Datastorage\DBTypes::DBBoolean;
			}
			else if($type == "binary[]")
			{
				$result = Core\Datastorage\DBTypes::DBArrayBinary;
			}
			else if($type == "date")
			{
				$result = Core\Datastorage\DBTypes::DBDate;
			}
			else
			{
				throw new Exception("The type $type is not managed.");
			}
		}
		
		Logger::Debug("GetDBType translate $type by $result");
		
		return $result;
	}
	
	/*
	* Execute the provided script
	*
	* @param string $script  The script
	* @param string $databaseName The database name
	*/
	private function executeScript($script, $databaseName)
	{
		$session = Core\Datastorage\Session::Singleton();
		$session->Open();

		// Select the current database
		$db_selected = mysql_select_db($databaseName);
		if (!$db_selected) 
		{
			throw new Exception('Cannot select database $databaseName : ' . mysql_error());
		}
		Logger::Debug("Script to execute  : " . $script);
		if (mysql_query($script)) 
		{
			Logger::Info("Script was successfully executed");
		} 
		else 
		{
			Logger::Error("Script failed : " . mysql_error());
		}
		
		$session->Close();
	}
}
?>